package org.xingole.jgit.utils;

/**
 * Conversion utilities for network byte order handling.
 *
 * <p>We can make up the common used codes into the static methods of the class so that we can make the method shared
 * by other classes and improve the code usage efficiency. Of course, we can implement the algorithm with kind design
 * pattern.</p>
 *
 */
public class NetworkByteHandler {

    /**
     * <p>Compare a 32 bit unsigned integer stored in a 32 bit signed integer.</p>
     *
     * <p>This function performs an unsigned compare operation, even though Java does not natively support
     * unsigned integer values. Negative numbers are treated as larger than positive ones.</p>
     *
     * @param a - the first value to compare
     * @param b - the second value to compare
     * @return : &lt; 0 if a &lt; b; 0 if a == b; &gt; 0 if a &gt; b.
     */
    public static int compareUInt32(final int a, final int b) {
        // step 1: we can first compare a and b without the consideration for the last bit.
        // So, we shift the a and b right only one bit with not sign moving.
        final int cmp = (a >>> 1) - (b >>> 1);
        // Directly, decide the relation through the high 31 bit comparision.
        if(cmp != 0)
            return cmp;
        // If the high 31 bit is the same, then we should continue comparing the last bit.
        return (a & 1) - (b & 1);
    }

    /**
     * <p>Compare a 64 bit unsigned integer stored in a 64 bit signed integer.</p>
     *
     * <p>This function performs an unsigned compare operation, even though Java does not natively support
     * unsigned integer values. Negative numbers are treated as larger than positive ones.</p>
     *
     * @param a - the first value to compare
     * @param b - the second value to compare
     * @return : &lt; 0 if a &lt; b; 0 if a == b; &gt; 0 if a &gt; b.
     */
    public static int compareUInt64(final long a, final long b) {
        // The procedure is hardly same with above situation
        long cmp = (a >>> 1) - (b >>> 1);

//        if(cmp != 0)
//            return (int) cmp;  if using the above same code, we must cast the cmp into int type but casting can make errors
        if(cmp > 0) {
            return 1;
        } else if(cmp < 0) {
            return -1;
        }

        cmp = ((a & 1) - (b & 1)); // As the assignment statement, we mustn't set the cmp as the final type variable.

        if(cmp > 0) {
            return 1;
        } else if(cmp < 0) {
            return -1;
        } else {
            return 0;
        }
    }

    /**
     * <p>Convert sequence of 2 bytes (network byte code order) into unsigned value.</p>
     * See
     * <a href="https://datatracker.ietf.org/doc/html/draft-newman-network-byte-order-01.txt">network byte code order</a>.
     * @param buf - buffer to acquire the 2 bytes of data from.
     * @param offset - position within the buffer to begin reading from.
     *                 This position and the next byte after it (for a total of 2 bytes) will be read.
     * @return : unsigned integer value that matches the 16 bits read.
     */
    public static int decodeUInt16(final byte[] buf, final int offset) {
        // The buf[offset] can automatically cast into int type through AND with 0xff.
        // step 1 : expand the shorter's bits into int compatible type.
        int r = (buf[offset] & 0xff) << 8;
        // step 2: shift the high byte into the left location.

        // step 3: split joint the low byte
        return r | (buf[offset + 1] & 0xff);
    }

    /**
     * <p>Convert sequence of 4 bytes (network byte code order) into unsigned value.</p>
     * See
     * <a href="https://datatracker.ietf.org/doc/html/draft-newman-network-byte-order-01.txt">network byte code order</a>.
     * @param buf - buffer to acquire the 4 bytes of data from.
     * @param offset - position within the buffer to begin reading from.
     *                 This position and the next 3 bytes after it (for a total of 4 bytes) will be read.
     * @return : unsigned integer value that matches the 32 bits read.
     */
    public static int decodeInt32(final byte[] buf, final int offset) {
        int r = (buf[offset] & 0xff) << 8;

        r |= buf[offset + 1] & 0xff;
        r <<= 8;

        r |= buf[offset + 2] & 0xff;
        return (r << 8) | (buf[offset + 3] & 0xff);
    }

    /**
     * Convert sequence of 8 bytes (network byte order) into signed value.
     *
     * @param intbuf
     *            buffer to acquire the 8 bytes of data from.
     * @param offset
     *            position within the buffer to begin reading from. This
     *            position and the next 7 bytes after it (for a total of 8
     *            bytes) will be read.
     * @return signed integer value that matches the 64 bits read.
     */
    public static long decodeInt64(final byte[] intbuf, final int offset) {
        long r = intbuf[offset] << 8;

        r |= intbuf[offset + 1] & 0xff;
        r <<= 8;

        r |= intbuf[offset + 2] & 0xff;
        r <<= 8;

        r |= intbuf[offset + 3] & 0xff;
        r <<= 8;

        r |= intbuf[offset + 4] & 0xff;
        r <<= 8;

        r |= intbuf[offset + 5] & 0xff;
        r <<= 8;

        r |= intbuf[offset + 6] & 0xff;
        return (r << 8) | (intbuf[offset + 7] & 0xff);
    }

    // the private constructor for this class
    private NetworkByteHandler() {
        // Don't create instance of a static only utility.
        super();
    }

    /************************************The encoding methods************************************************/

    /**
     * Write a 32 bit integer as a sequence of 4 bytes (network byte order).
     *
     * @param intbuf - buffer to write the 4 bytes of data into
     * @param offset - position within the buffer to begin writing to.
     *               This position and the next 3 bytes after it (for a total of 4 bytes) will be replaced.
     * @param v - the value to write.
     */
    public static void encodeInt32(final byte[] intbuf, final int offset, int v) {
        intbuf[offset + 3] = (byte) v;  // extract the lower 8 bits using automatic convert

        v >>>= 8;
        intbuf[offset + 2] = (byte) v;

        v >>>= 8;
        intbuf[offset + 1] = (byte) v;

        v >>>= 8;
        intbuf[offset] = (byte) v;
    }
}
